Modelo híbrido para predecir el Precio del Gas Natural#
ARIMA (Autoregressive Integrated Moving Average), es un modelo estadístico clásico utilizado para analizar y predecir series de tiempo estacionarias o que se pueden hacer estacionarias mediante diferenciación. ARIMA(p,d,q) tiene tres componentes principales:
Autorregresivo (AR): Utiliza valores pasados de la serie para predecir valores futuros. p es el orden del modelo AR, que representa el número de valores pasados utilizados.
Integrado (I): La diferenciación (d) se aplica a la serie para lograr la estacionariedad, es decir, eliminar tendencias y estacionalidad.
Media móvil (MA): Utiliza los errores pasados (residuos) de la serie para predecir valores futuros. q es el orden del modelo MA, que indica el número de errores pasados.
ARIMA es eficaz para predecir series de tiempo con tendencias y patrones estacionales claros. Su fortaleza reside en su base teórica sólida y su capacidad para capturar relaciones lineales entre valores pasados y futuros. Sin embargo, ARIMA puede tener limitaciones al modelar no linealidades complejas.
Por otro lado, Perceptrón Multicapa (MLP, por sus siglas en inglés Multilayer Perceptron) es uno de los modelos de redes neuronales artificiales más básicos y fundamentales. Como una extensión del perceptrón simple, el MLP forma parte de la categoría de redes neuronales feedforward, caracterizándose por una estructura de múltiples capas (de entrada, ocultas y de salida) con conexiones entre las neuronas de cada capa adyacente. Estas conexiones tienen pesos asociados que se ajustan durante el proceso de entrenamiento, generalmente utilizando el algoritmo de retropropagación. El MLP emplea funciones de activación no lineales, lo que le permite modelar relaciones complejas entre los datos de entrada y de salida. Debido a su versatilidad, los MLPs han encontrado aplicación en una amplia variedad de tareas, como clasificación, regresión, reconocimiento de patrones, aproximación de funciones, procesamiento de lenguaje natural, sistemas de recomendación, control automático, finanzas, medicina y compresión de datos, entre otras. A pesar de que en ciertas aplicaciones han sido superados por arquitecturas más avanzadas, los MLPs siguen siendo una herramienta valiosa y ampliamente utilizada en el campo del aprendizaje automático.
Carga y Procesamiento de los datos#
Antes de desarrollar los modelos, comenzamos importando las librerías requeridas y cargando la base de datos de precios de cierre del gas natural. Posteriormente convertimos el formato de la columna ‘date’a formato datetime y se establece como índice. Acto seguido se extrae la columana close como variable objetivo.
import pandas as pd
import numpy as np
from statsmodels.tsa.arima.model import ARIMA
from sklearn.model_selection import TimeSeriesSplit, RandomizedSearchCV, train_test_split
from sklearn.metrics import mean_squared_error, mean_absolute_percentage_error
import matplotlib.pyplot as plt
import plotly.io as pio
import plotly.express as px
import plotly.offline as py
pio.renderers.default = "notebook"
import warnings
warnings.filterwarnings("ignore")
data=pd.read_csv(r"C:\Users\claud\OneDrive - Universidad del Norte\Escritorio\Series de tiempo\Natural_Gas_data.csv")
data['date'] = pd.to_datetime(data['date'])
data.set_index('date', inplace=True)
data_index = data.index
Partición de los datos en entrenamiento, prueba y test#
Dividimos los datos en conjuntos de entrenamiento, validación y prueba y a continuación, visualizamos la partición de datos gráficamente.
y = data['close'].values
# Primero, dividimos en train + val y test (85% para train + val, 15% para test)
y_train_val, y_test = train_test_split(y, test_size=0.15, shuffle=False)
# Luego, dividimos el train + val en train (70%) y val (30% de ese subconjunto)
y_train, y_val = train_test_split(y_train_val, test_size=0.3, shuffle=False)
# Verificando las formas de los conjuntos
print(f'Tamaño de entrenamiento: {len(y_train)}')
print(f'Tamaño de validación: {len(y_val)}')
print(f'Tamaño de prueba: {len(y_test)}')
Tamaño de entrenamiento: 3558
Tamaño de validación: 1525
Tamaño de prueba: 897
data_index_full = data_index[:len(y_train) + len(y_val) + len(y_test)]
# Extender las listas para alinear los datos con el índice completo
y_train_extended = np.concatenate([y_train, [None] * (len(y_val) + len(y_test))])
y_val_extended = np.concatenate([[None] * len(y_train), y_val, [None] * len(y_test)])
y_test_extended = np.concatenate([[None] * (len(y_train) + len(y_val)), y_test])
# Crear DataFrame para la gráfica
df_plot = pd.DataFrame({
'Fecha': data_index_full,
'Datos de entrenamiento': y_train_extended,
'Datos de validación': y_val_extended,
'Datos de prueba': y_test_extended,
})
# Graficar con Plotly
fig = px.line(
df_plot, x='Fecha',
y=[
'Datos de entrenamiento',
'Datos de validación',
'Datos de prueba'
],
title="Particiones serie: precio de cierre gas natural", labels={'value': 'Valor', 'Fecha': 'Fecha'}
)
fig.show()
Conjunto de Entrenamiento (Azul): Cubre la mayor parte de la serie temporal, desde aproximadamente 2003 hasta mediados de 2015. Muestra una alta variabilidad en los precios, con picos y valles significativos. Esta sección se utiliza para entrenar los modelos ARIMA y MLP.
Conjunto de Validación (Naranja): Se extiende desde mediados de 2015 hasta aproximadamente 2020. Los precios en este periodo muestran menor variabilidad que en el conjunto de entrenamiento, presentando fluctuaciones menos pronunciadas. Se utiliza para validar los modelos y ajustar los hiperparámetros.
Conjunto de Prueba (Verde): Cubre el periodo final desde aproximadamente 2020 hasta 2022. Similar al conjunto de validación, presenta fluctuaciones en los precios, pero se usa exclusivamente para evaluar el rendimiento final del modelo ya entrenado y validado, sin usar información de este periodo para optimizar los modelos.
Observaciones:
La división en conjuntos de entrenamiento, validación y prueba parece mantener un orden cronológico, lo cual es crucial para series de tiempo para evitar fugas de información al futuro.
La proporción entre los conjuntos puede verse aproximada, un adecuado balanceo es importante para la generalización del modelo.
La imagen sugiere que la serie de tiempo presenta una cierta estacionalidad o tendencias, pero se necesita más información para confirmarlo. La estacionalidad puede afectar la elección del modelo y sus parámetros.
Definición de métricas, validación de supuestos y gráfica de resultados#
A continuación, se definen las métricas para cada conjunto de datos y almacenamos las métricas en la lista metrics_list para su comparación y se define la verificación de los supuestos del modelo mediante el cálculo de residuales, la prueba de Shapiro-Wilk (Normalidad), Ljung-box (Autocorrelación) y Breusch-Pagan (Homocedasticidad).
Finalmente, graficamos los resultados mediante un histograma, Q-Q plot y ACF de los residuales de cada conjunto de datos.
def calculate_and_store_metrics(model, model_name, y_train, y_train_pred, y_val, y_val_pred, y_test, y_pred):
# Cálculo de métricas para el conjunto de prueba
mape_test = mean_absolute_percentage_error(y_test, y_pred)
rmse_test = mean_squared_error(y_test, y_pred, squared=False)
# Cálculo de métricas para el conjunto de validación
mape_val = mean_absolute_percentage_error(y_val, y_val_pred)
rmse_val = mean_squared_error(y_val, y_val_pred, squared=False)
# Almacenando las métricas en la lista
metrics_list.append({
'Modelo': model_name,
'MAPE Prueba': mape_test,
'RMSE Prueba': rmse_test,
'MAPE Validación': mape_val,
'RMSE Validación': rmse_val
})
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import statsmodels.api as sm
from scipy.stats import shapiro # Corrección: Usamos shapiro desde scipy
def check_model_assumptions(model, y_train, y_train_pred, y_val, y_val_pred, y_test, y_test_pred):
# Calcular los residuales
resid_train = y_train - y_train_pred
resid_val = y_val - y_val_pred
resid_test = y_test - y_test_pred
print("\n----- Pruebas de Hipótesis sobre los Supuestos -----")
# Prueba de Normalidad (Shapiro-Wilk) para entrenamiento
stat, p_value = shapiro(resid_train)
print(f"Shapiro-Wilk p-value (Entrenamiento): {p_value:.4f}")
print("Conclusión:", "NO sigue una distribución normal.\n" if p_value < 0.05 else "Sigue una distribución normal.\n")
# Prueba de Autocorrelación (Ljung-Box) para entrenamiento
lb_test = sm.stats.acorr_ljungbox(resid_train, lags=[10], return_df=True)
lb_p_value = lb_test['lb_pvalue'].values[0]
print(f"Ljung-Box p-value (Entrenamiento): {lb_p_value:.4f}")
print("Conclusión:", "Autocorrelación significativa.\n" if lb_p_value < 0.05 else "Sin autocorrelación significativa.\n")
# Prueba de Homocedasticidad (Breusch-Pagan) para entrenamiento
exog = sm.add_constant(np.arange(len(resid_train)).reshape(-1, 1))
_, bp_p_value, _, _ = sm.stats.het_breuschpagan(resid_train, exog)
print(f"Breusch-Pagan p-value (Entrenamiento): {bp_p_value:.4f}")
print("Conclusión:", "Varianza no constante.\n" if bp_p_value < 0.05 else "Varianza constante.\n")
# Crear figuras en Matplotlib
fig, axes = plt.subplots(2, 3, figsize=(18, 10))
fig.suptitle('Verificación de los Supuestos del Modelo')
# Histograma de los residuales de entrenamiento
axes[0, 0].hist(resid_train, bins=20, edgecolor='black')
axes[0, 0].set_title('Histograma (Entrenamiento)')
axes[0, 0].set_xlabel('Residual')
axes[0, 0].set_ylabel('Frecuencia')
# Q-Q Plot de los residuales de entrenamiento
sm.qqplot(resid_train, line='s', ax=axes[0, 1])
axes[0, 1].set_title('Q-Q Plot (Entrenamiento)')
# ACF de los residuales de entrenamiento
sm.graphics.tsa.plot_acf(resid_train, ax=axes[0, 2], lags=30)
axes[0, 2].set_title('ACF (Entrenamiento)')
# Histograma de los residuales de validación
axes[1, 0].hist(resid_val, bins=20, edgecolor='black')
axes[1, 0].set_title('Histograma (Validación)')
axes[1, 0].set_xlabel('Residual')
axes[1, 0].set_ylabel('Frecuencia')
# Q-Q Plot de los residuales de validación
sm.qqplot(resid_val, line='s', ax=axes[1, 1])
axes[1, 1].set_title('Q-Q Plot (Validación)')
# ACF de los residuales de validación
sm.graphics.tsa.plot_acf(resid_val, ax=axes[1, 2], lags=30)
axes[1, 2].set_title('ACF (Validación)')
plt.tight_layout(rect=[0, 0, 1, 0.96]) # Ajustar layout para evitar superposición
plt.show()
def plot_model_results_plotly(y_train, y_train_pred, y_val, y_val_pred, y_test, y_pred, data_index, train_size, val_size, title):
# Crear un índice completo basado en la longitud total de los datos
data_index_full = data_index[:len(y_train) + len(y_val) + len(y_test)]
# Extender las listas para alinear los datos con el índice completo
y_train_extended = np.concatenate([y_train, [None] * (len(y_val) + len(y_test))])
y_train_pred_full = np.concatenate([y_train_pred, [None] * (len(y_train_extended) - len(y_train_pred))])
y_val_extended = np.concatenate([[None] * len(y_train), y_val, [None] * len(y_test)])
y_val_pred_full = np.concatenate([[None] * len(y_train), y_val_pred, [None] * (len(y_test))])
y_test_extended = np.concatenate([[None] * (len(y_train) + len(y_val)), y_test])
y_pred_full = np.concatenate([[None] * (len(y_train) + len(y_val)), y_pred])
# Crear DataFrame para la gráfica
df_plot = pd.DataFrame({
'Fecha': data_index_full,
'Datos de entrenamiento': y_train_extended,
'Modelo entrenado': y_train_pred_full,
'Datos de validación': y_val_extended,
'Predicción validación': y_val_pred_full,
'Datos de prueba': y_test_extended,
'Predicción prueba': y_pred_full
})
# Graficar con Plotly
fig = px.line(
df_plot, x='Fecha',
y=[
'Datos de entrenamiento', 'Modelo entrenado',
'Datos de validación', 'Predicción validación',
'Datos de prueba', 'Predicción prueba'
],
title=title, labels={'value': 'Valor', 'Fecha': 'Fecha'}
)
fig.show()
Modelo ARIMA#
Luego, se crea un modelo ARIMA con el orden (5, 1, 0) usando la serie de precios de cierre. El orden (p, d, q) representa el orden autorregresivo (p), el grado de diferenciación (d) y el orden de media móvil (q). (5,1,0) indica un modelo AR(5) con una diferenciación de primer orden.
El modelo se ajusta a los datos usando model_arima.fit(), se generan predicciones para toda la serie temporal y se calculan los residuos restando las predicciones del modelo ARIMA de los valores reales.
Se implementa un pronóstico móvil para los conjuntos de validación y prueba usando ARIMA.
Se define una función para optimizar los parámetros del modelo ARIMA basándose en el criterio AIC (Criterio de información de Akaike), evitando a su vez el sobreajuste.
Se evalúan los diferentes modelos (AR, MA, ARIMA) y se selecciona el mejor basado en AIC.
Se entrena el mejor modelo y se evalúa su rendimiento en los conjuntos de validación y prueba.
def rolling_forecast(order, y_train, y_val, y_test):
history = list(y_train) # Usar datos de entrenamiento como historial inicial
val_predictions = []
test_predictions = []
# Predecir para el conjunto de validación
for t in range(len(y_val)):
model = ARIMA(history, order=order).fit()
yhat = model.forecast()[0]
val_predictions.append(yhat)
history.append(y_val[t]) # Actualizar el historial con el dato real
# Predecir para el conjunto de prueba
for t in range(len(y_test)):
model = ARIMA(history, order=order).fit()
yhat = model.forecast()[0]
test_predictions.append(yhat)
history.append(y_test[t]) # Actualizar el historial con el dato real
return np.array(val_predictions), np.array(test_predictions)
def optimize_model(order_range, y_train):
"""Optimiza el modelo ARIMA para los parámetros dados."""
best_aic = float("inf")
best_params = None
best_model = None
for p in order_range[0]:
for d in order_range[1]:
for q in order_range[2]:
try:
model = ARIMA(y_train, order=(p, d, q)).fit()
aic = model.aic
if aic < best_aic:
best_aic = aic
best_params = (p, d, q)
best_model = model
except Exception as e:
print(f"Error: {e}")
continue
print(f"Mejores parámetros: p={best_params[0]}, d={best_params[1]}, q={best_params[2]} con AIC={best_aic}")
return best_model, best_params
def evaluate_models(y_train, y_val, y_test, data):
"""Evalúa todos los modelos y selecciona el mejor."""
models = [
('AR', (p_values, [0], [0])),
('MA', ([0], [0], q_values)),
('ARIMA', (p_values, d_values, q_values))
]
best_aic = float("inf")
best_model_name = None
best_model = None
best_params = None
for model_name, order_range in models:
print(f"Evaluando: {model_name}")
model, params = optimize_model(order_range, y_train)
if model.aic < best_aic:
best_aic = model.aic
best_model_name = model_name
best_model = model
best_params = params
print(f"\nEl mejor modelo es: {best_model_name} con AIC={best_aic}")
# Rolling forecast para validación y prueba
y_val_pred, y_test_pred = rolling_forecast(best_params, y_train, y_val, y_test)
y_train_pred = best_model.fittedvalues
# Calcular y almacenar métricas
calculate_and_store_metrics(best_model, best_model_name, y_train, y_train_pred, y_val, y_val_pred, y_test, y_test_pred)
# Graficar los resultados
plot_model_results_plotly(y_train, y_train_pred, y_val, y_val_pred, y_test, y_test_pred, data.index, len(y_train), len(y_val), best_model_name)
check_model_assumptions(best_model, y_train, y_train_pred, y_val, y_val_pred, y_test, y_test_pred)
return(best_model, best_model_name, y_train_pred, y_val_pred, y_test_pred)
metrics_list=[]
p_values = range(0, 3)
d_values = range(0, 2)
q_values = range(0, 6)
best_model, best_model_name, y_train_pred, y_val_pred, y_test_pred = evaluate_models(y_train, y_val, y_test, data)
Evaluando: AR
Mejores parámetros: p=1, d=0, q=0 con AIC=-911.4617908954825
Evaluando: MA
Mejores parámetros: p=0, d=0, q=5 con AIC=3438.3500291627465
Evaluando: ARIMA
Mejores parámetros: p=2, d=1, q=5 con AIC=-923.2849978518195
El mejor modelo es: ARIMA con AIC=-923.2849978518195
----- Pruebas de Hipótesis sobre los Supuestos -----
Shapiro-Wilk p-value (Entrenamiento): 0.0000
Conclusión: NO sigue una distribución normal.
Ljung-Box p-value (Entrenamiento): 0.9600
Conclusión: Sin autocorrelación significativa.
Breusch-Pagan p-value (Entrenamiento): 0.0000
Conclusión: Varianza no constante.
1. Selección del Modelo:
El código probó tres tipos de modelos:
AR (Autoregresivo): El mejor modelo AR encontrado tuvo un orden p=1, d=0, q=0 y un AIC de -911.46.
MA (Media Móvil): El mejor modelo MA tuvo un orden p=0, d=0, q=5 y un AIC de -3438.35.
ARIMA (Autoregresivo Integrado de Media Móvil): El mejor modelo ARIMA tuvo un orden p=2, d=1, q=5 y un AIC de -923.28. El AIC (Criterio de Información de Akaike) es una medida que se utiliza para comparar modelos. Un valor de AIC menor indica un mejor ajuste al conjunto de datos. En este caso, el modelo ARIMA (p=2, d=1, q=5) tiene el AIC más bajo (-923.28), por lo que se seleccionó como el mejor modelo.
2. Gráfica de Resultados:
La gráfica muestra las predicciones del mejor modelo ARIMA (línea naranja) comparadas con los datos reales (líneas azul y verde). La línea azul representa el conjunto de entrenamiento, la línea verde representa el conjunto de prueba, y la línea naranja representa las predicciones del modelo. Se puede observar visualmente la calidad del ajuste del modelo a los datos de entrenamiento y la capacidad predictiva a los datos de prueba.
3. Pruebas de Hipótesis:
Se realizaron tres pruebas de hipótesis sobre los residuales (los errores del modelo) para evaluar si cumplen con los supuestos de los modelos ARIMA:
Shapiro-Wilk: Esta prueba evalúa la normalidad de los residuales. Un p-valor de 0.0000 indica que se rechaza la hipótesis de normalidad. Esto implica que los residuales no siguen una distribución normal.
Ljung-Box: Esta prueba verifica la autocorrelación en los residuales. Un p-valor de 0.9660 indica que no hay autocorrelación significativa en los residuos. Esto es positivo, ya que indica que el modelo captura bien la estructura de autocorrelación de los datos.
Breusch-Pagan: Esta prueba evalúa la homocedasticidad (varianza constante) de los residuales. Un p-valor de 0.0000 indica que se rechaza la hipótesis de homocedasticidad. Los residuales presentan varianza no constante.
4. Verificación de Supuestos
Para ambos conjuntos (Entrenamiento y Validación):
Histograma: Los histogramas muestran la distribución de los residuales. Idealmente, la distribución debería ser aproximadamente normal y centrada alrededor de cero. En ambos conjuntos, se observa una leve asimetría (no es una distribución perfectamente simétrica), y una concentración alrededor de cero, si bien no se ajusta a una curva normal perfecta.
Q-Q Plot (Gráfico de cuantiles-cuantiles): Estos gráficos comparan los cuantiles de la distribución empírica de los residuales con los cuantiles de una distribución normal teórica. Si los residuales son normales, los puntos deberían caer aproximadamente sobre la línea diagonal roja. En ambos conjuntos, hay algunos puntos que se desvían ligeramente de la línea, especialmente en los extremos, lo que sugiere una desviación de la normalidad, aunque no es extremadamente severa.
ACF (Función de Autocorrelación): Las gráficas ACF muestran la autocorrelación entre los residuales a diferentes lags (retrasos). Si el modelo ARIMA es adecuado, las barras de autocorrelación en los residuales deberían estar dentro del intervalo de confianza (las áreas sombreadas en azul). Ambas gráficas muestran que la mayoría de las barras están dentro del intervalo de confianza, aunque hay algunos lags con valores que se aproximan a los límites del intervalo. Esto sugiere una posible autocorrelación residual, aunque no muy fuerte.
Conclusiones
En general, los diagnósticos sugieren algunas desviaciones de los supuestos clásicos del modelo ARIMA:
No normalidad: Los histogramas y los Q-Q plots muestran una leve desviación de la normalidad, aunque no es extrema. Posible autocorrelación residual: El ACF muestra alguna autocorrelación residual, aunque relativamente débil.
Estas desviaciones pueden afectar la precisión de las inferencias estadísticas basadas en el modelo ARIMA (como los intervalos de confianza). Sin embargo, para predicciones, la no normalidad no es siempre un problema crítico, especialmente si la cantidad de datos es grande. Aunque el modelo ARIMA podría brindar predicciones razonablemente precisas, el diagnóstico de los residuales indica áreas para mejorar el modelo o considerar alternativas para lograr un mejor cumplimiento de los supuestos.
Adicionalmente, el modelo ARIMA (p=2, d=1, q=5) seleccionado tiene un AIC bajo, lo que sugiere un buen ajuste a los datos de entrenamiento.
Análisis de residuos#
Calculamos y visualizamos los residuos del modelo para los conjuntos de entrenamiento, validación y prueba.
resid_train = y_train - y_train_pred
resid_val = y_val - y_val_pred
resid_test = y_test - y_test_pred
#g residuos
data_index_full = data_index[:len(resid_train) + len(resid_val) + len(resid_test)]
# Extender las listas para alinear los datos con el índice completo
y_train_extended = np.concatenate([resid_train, [None] * (len(resid_val) + len(resid_test))])
y_val_extended = np.concatenate([[None] * len(resid_train), resid_val, [None] * len(resid_test)])
y_test_extended = np.concatenate([[None] * (len(resid_train) + len(resid_val)), resid_test])
# Crear DataFrame para la gráfica
df_plot = pd.DataFrame({
'Fecha': data_index_full,
'resid de entrenamiento': y_train_extended,
'resid de validación': y_val_extended,
'resid de prueba': y_test_extended,
})
# Graficar con Plotly
fig = px.line(
df_plot, x='Fecha',
y=[
'resid de entrenamiento',
'resid de validación',
'resid de prueba'
],
title="Particiones serie: precio de cierre gas natural", labels={'value': 'Valor', 'Fecha': 'Fecha'}
)
fig.show()
Esta gráfica muestra los residuales del modelo ARIMA para los conjuntos de entrenamiento, validación y prueba a lo largo del tiempo. Los residuales son las diferencias entre los valores reales y los valores predichos por el modelo.
Azul (Residuos de entrenamiento): Representa los residuales calculados a partir de la parte de la serie de tiempo utilizada para entrenar el modelo ARIMA. Idealmente, estos residuales deberían estar aleatoriamente distribuidos alrededor de cero, con una varianza constante a lo largo del tiempo, y sin patrones o tendencias visibles. La gráfica indica una distribución predominantemente cercana a cero, pero con una alta variabilidad y algunos valores atípicos que se desvían significativamente de la media.
Naranja (Residuos de validación): Muestra los residuales en el conjunto de validación. Similarmente al conjunto de entrenamiento, los residuales en la validación deberían mostrar aleatoriedad y una varianza constante alrededor de cero, sin patrones. Similar al conjunto de entrenamiento, se observa una alta volatilidad y algunos puntos alejados de cero.
Verde (Residuos de prueba): Representa los residuales en el conjunto de prueba. Esta sección es crucial porque evalúa la capacidad predictiva del modelo en datos nunca antes vistos. Los residuales en la prueba deberían mostrar las mismas características de aleatoriedad y varianza constante que en los conjuntos anteriores. Si hay patrones o tendencias, puede indicar un modelo mal especificado o un sobreajuste. Se observa una variabilidad similar a los conjuntos de entrenamiento y validación, con algunos picos.
Modelado con red neuronal MLP (Perceptón Multicapa)#
Ahora tomamos los residuos del modelo para hacer el modelo de redes neuronales.
Se define una función para crear ventanas deslizantes de los datos.
Se crea y entrena el modelo MLP con diferentes combinaciones de hiperparámetros.
Se evalúa el rendimiento del modelo en los conjuntos de entrenamiento, validación y prueba.
import numpy as np
import pandas as pd
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
from tensorflow.keras.callbacks import EarlyStopping
from sklearn.metrics import mean_squared_error
# Paso 1: Función para crear X e y usando ventanas deslizantes
def makeXy(ts, nb_timesteps):
"""
Transforma una serie de tiempo en conjuntos X (regresores) e y (objetivo) usando ventanas deslizantes.
"""
X = []
y = []
for i in range(nb_timesteps, ts.shape[0]):
X.append(list(ts[i - nb_timesteps:i])) # Ventana deslizante como regresores
y.append(ts[i]) # El siguiente valor como objetivo
X, y = np.array(X), np.array(y)
return X, y
# Paso 2: Función para crear el modelo MLP
def create_mlp_model(input_shape, neurons, dropout_rate):
model = Sequential()
model.add(Dense(units=neurons[0], activation='relu', input_shape=(input_shape,)))
model.add(Dropout(dropout_rate))
model.add(Dense(units=neurons[1], activation='relu'))
model.add(Dropout(dropout_rate))
model.add(Dense(units=1)) # Capa de salida
model.compile(optimizer='adam', loss='mean_squared_error')
return model
# Paso 3: Función para entrenar y evaluar modelos con diferentes combinaciones de hiperparámetros
def train_and_evaluate_models(X_train, y_train, X_val, y_val, neuron_combinations, dropout_combinations, batch_sizes, epochs=50):
results = []
best_model = None
best_val_loss = float('inf')
best_params = {}
for neurons in neuron_combinations:
for dropout_rate in dropout_combinations:
for batch_size in batch_sizes:
# Crear el modelo con la configuración actual
model = create_mlp_model(X_train.shape[1], neurons, dropout_rate)
# Entrenar el modelo con EarlyStopping
history = model.fit(
X_train, y_train,
epochs=epochs,
batch_size=batch_size,
validation_data=(X_val, y_val),
verbose=0,
callbacks=[EarlyStopping(patience=10, restore_best_weights=True)]
)
# Evaluar el modelo en los datos de validación
val_loss = model.evaluate(X_val, y_val, verbose=0)
# Guardar los resultados de esta combinación
results.append({
'neurons': neurons,
'dropout_rate': dropout_rate,
'batch_size': batch_size,
'val_loss': val_loss
})
# Verificar si este modelo tiene la mejor pérdida de validación
if val_loss < best_val_loss:
best_val_loss = val_loss
best_model = model
best_params = {
'neurons': neurons,
'dropout_rate': dropout_rate,
'batch_size': batch_size
}
# Imprimir el progreso
print(f"Neurons: {neurons}, Dropout: {dropout_rate}, Batch Size: {batch_size}, Validation Loss: {val_loss}")
# Convertir resultados a un DataFrame para una mejor visualización
results_df = pd.DataFrame(results)
print("\nMejor modelo:")
print(f"Neurons: {best_params['neurons']}, Dropout: {best_params['dropout_rate']}, Batch Size: {best_params['batch_size']}, Validation Loss: {best_val_loss}")
return best_model, results_df, best_params
# Ejemplo de uso de la función
# Supongamos que resid_train, resid_val, resid_test están definidos como series de tiempo de numpy
# Configuración de parámetros
nb_timesteps = 5 # Número de pasos de tiempo en cada ventana deslizante
neuron_combinations = [(64, 32), (128, 64), (32, 16)]
dropout_combinations = [0.2, 0.3, 0.4]
batch_sizes = [16, 32, 64]
# Crear X e y para train, val y test usando la función makeXy
X_train_resd, y_train_resd = makeXy(resid_train, nb_timesteps)
X_val_resd, y_val_resd = makeXy(resid_val, nb_timesteps)
X_test_resd, y_test_resd = makeXy(resid_test, nb_timesteps)
# Entrenar el modelo y obtener los mejores parámetros
best_model_resd, results_df_resd, best_params_resd = train_and_evaluate_models(X_train_resd, y_train_resd, X_val_resd, y_val_resd, neuron_combinations, dropout_combinations, batch_sizes)
Neurons: (64, 32), Dropout: 0.2, Batch Size: 16, Validation Loss: 0.007824108935892582
Neurons: (64, 32), Dropout: 0.2, Batch Size: 32, Validation Loss: 0.007852353155612946
Neurons: (64, 32), Dropout: 0.2, Batch Size: 64, Validation Loss: 0.00779171846807003
Neurons: (64, 32), Dropout: 0.3, Batch Size: 16, Validation Loss: 0.007849524728953838
Neurons: (64, 32), Dropout: 0.3, Batch Size: 32, Validation Loss: 0.00781164038926363
Neurons: (64, 32), Dropout: 0.3, Batch Size: 64, Validation Loss: 0.007905117236077785
Neurons: (64, 32), Dropout: 0.4, Batch Size: 16, Validation Loss: 0.007881257683038712
Neurons: (64, 32), Dropout: 0.4, Batch Size: 32, Validation Loss: 0.007807622663676739
Neurons: (64, 32), Dropout: 0.4, Batch Size: 64, Validation Loss: 0.00783879030495882
Neurons: (128, 64), Dropout: 0.2, Batch Size: 16, Validation Loss: 0.007814438082277775
Neurons: (128, 64), Dropout: 0.2, Batch Size: 32, Validation Loss: 0.007789664436131716
Neurons: (128, 64), Dropout: 0.2, Batch Size: 64, Validation Loss: 0.007861248217523098
Neurons: (128, 64), Dropout: 0.3, Batch Size: 16, Validation Loss: 0.007826651446521282
Neurons: (128, 64), Dropout: 0.3, Batch Size: 32, Validation Loss: 0.00773252546787262
Neurons: (128, 64), Dropout: 0.3, Batch Size: 64, Validation Loss: 0.007812670432031155
Neurons: (128, 64), Dropout: 0.4, Batch Size: 16, Validation Loss: 0.007832692004740238
Neurons: (128, 64), Dropout: 0.4, Batch Size: 32, Validation Loss: 0.007816466502845287
Neurons: (128, 64), Dropout: 0.4, Batch Size: 64, Validation Loss: 0.007841799408197403
Neurons: (32, 16), Dropout: 0.2, Batch Size: 16, Validation Loss: 0.007831436581909657
Neurons: (32, 16), Dropout: 0.2, Batch Size: 32, Validation Loss: 0.00782731268554926
Neurons: (32, 16), Dropout: 0.2, Batch Size: 64, Validation Loss: 0.007894482463598251
Neurons: (32, 16), Dropout: 0.3, Batch Size: 16, Validation Loss: 0.007827219553291798
Neurons: (32, 16), Dropout: 0.3, Batch Size: 32, Validation Loss: 0.0077741555869579315
Neurons: (32, 16), Dropout: 0.3, Batch Size: 64, Validation Loss: 0.007820922881364822
Neurons: (32, 16), Dropout: 0.4, Batch Size: 16, Validation Loss: 0.007872060872614384
Neurons: (32, 16), Dropout: 0.4, Batch Size: 32, Validation Loss: 0.00786652509123087
Neurons: (32, 16), Dropout: 0.4, Batch Size: 64, Validation Loss: 0.007905959151685238
Mejor modelo:
Neurons: (128, 64), Dropout: 0.3, Batch Size: 32, Validation Loss: 0.00773252546787262
Para modelar los residuos del modelo ARIMA con, se probaron diferentes combinaciones de hiperparámetros y se evaluó su rendimiento utilizando la pérdida de validación (Validation Loss). La pérdida de validación es el error del modelo en un conjunto de datos de validación que no se usó durante el entrenamiento, lo que ayuda a evitar el sobreajuste. Un valor más bajo indica un mejor rendimiento del modelo.
Se probaron tres configuraciones de neuronas, tres tasas de dropout y tres tamaños de batch, lo que da un total de 27 modelos.
El mejor modelo fue el que obtuvo la menor pérdida de validación, que es:*
Neurons: (128, 64) Dropout: 0.3 Batch Size: 32 Validation Loss: 0.007732
Esto indica que la mejor configuración de la MLP para este problema específico, según los experimentos llevados a cabo, es una red con dos capas densas (128 y 64 neuronas respectivamente), una tasa de dropout del 30% y un tamaño de batch de 32. Esta configuración resultó en un error de validación de 0.007732, que es el valor más bajo entre todas las configuraciones probadas.
* Arquitectura de la mejor red neuronal
best_model_resd.summary()
Model: "sequential_67"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_201 (Dense) (None, 128) 768
dropout_134 (Dropout) (None, 128) 0
dense_202 (Dense) (None, 64) 8256
dropout_135 (Dropout) (None, 64) 0
dense_203 (Dense) (None, 1) 65
=================================================================
Total params: 9,089
Trainable params: 9,089
Non-trainable params: 0
_________________________________________________________________
# Predicciones en train, val y test
train_predictions_resd = best_model_resd.predict(X_train_resd).ravel()
val_predictions_resd = best_model_resd.predict(X_val_resd).ravel()
test_predictions_resd = best_model_resd.predict(X_test_resd).ravel()
112/112 [==============================] - 0s 1ms/step
48/48 [==============================] - 0s 822us/step
28/28 [==============================] - 0s 880us/step
La red neuronal hizo predicciones para 112 muestras del conjunto de entrenamiento, 48 muestras para el conjunto de validación y 28 muestras para el conjunto de prueba.
La velocidad de predicción es bastante rápida, ya que el tiempo por paso es del orden de milisegundos o microsegundos. Esto indica que el modelo es computacionalmente eficiente para realizar predicciones.
# Calcular el MSE en cada conjunto
train_mse_resd = mean_squared_error(y_train_resd, train_predictions_resd)
val_mse_resd = mean_squared_error(y_val_resd, val_predictions_resd)
test_mse_resd = mean_squared_error(y_test_resd, test_predictions_resd)
print("\nMSE en conjunto de entrenamiento:", train_mse_resd)
print("MSE en conjunto de validación:", val_mse_resd)
print("MSE en conjunto de prueba:", test_mse_resd)
MSE en conjunto de entrenamiento: 0.04327132513049912
MSE en conjunto de validación: 0.007732527109247361
MSE en conjunto de prueba: 0.05698560495812593
MSE en conjunto de entrenamiento: 0.04327132513049912: Este es el MSE en el conjunto de datos utilizado para entrenar el modelo. Es un valor relativamente alto.
MSE en conjunto de validación: 0.007732527109247361: Este es el MSE en el conjunto de datos de validación. Es significativamente más bajo que el MSE de entrenamiento, lo que sugiere que el modelo generaliza bien a datos nuevos.
MSE en conjunto de prueba: 0.05698560495812593: Este es el MSE en el conjunto de datos de prueba. Es interesante notar que este valor es bastante más alto que el MSE de validación.
# Mostrar los resultados ordenados por la pérdida de validación
results_df = results_df_resd.sort_values(by='val_loss', ascending=True)
print(results_df)
neurons dropout_rate batch_size val_loss
13 (128, 64) 0.3 32 0.007733
22 (32, 16) 0.3 32 0.007774
10 (128, 64) 0.2 32 0.007790
2 (64, 32) 0.2 64 0.007792
7 (64, 32) 0.4 32 0.007808
4 (64, 32) 0.3 32 0.007812
14 (128, 64) 0.3 64 0.007813
9 (128, 64) 0.2 16 0.007814
16 (128, 64) 0.4 32 0.007816
23 (32, 16) 0.3 64 0.007821
0 (64, 32) 0.2 16 0.007824
12 (128, 64) 0.3 16 0.007827
21 (32, 16) 0.3 16 0.007827
19 (32, 16) 0.2 32 0.007827
18 (32, 16) 0.2 16 0.007831
15 (128, 64) 0.4 16 0.007833
8 (64, 32) 0.4 64 0.007839
17 (128, 64) 0.4 64 0.007842
3 (64, 32) 0.3 16 0.007850
1 (64, 32) 0.2 32 0.007852
11 (128, 64) 0.2 64 0.007861
25 (32, 16) 0.4 32 0.007867
24 (32, 16) 0.4 16 0.007872
6 (64, 32) 0.4 16 0.007881
20 (32, 16) 0.2 64 0.007894
5 (64, 32) 0.3 64 0.007905
26 (32, 16) 0.4 64 0.007906
Esta tabla muestra los resultados de la búsqueda de hiperparámetros para el modelo MLP. Cada fila representa una configuración diferente probada, y las columnas muestran los hiperparámetros y la pérdida de validación resultante. Esto facilita la identificación de las mejores configuraciones.
Observando la tabla, podemos extraer las siguientes conclusiones:
La configuración con la menor pérdida de validación (val_loss) es (128, 64) neuronas, 0.3 de dropout y un batch_size de 32, con un val_loss de 0.007733.
Influencia de los hiperparámetros: Se puede observar que, dentro de los rangos probados, la arquitectura con 128 y 64 neuronas tiende a dar mejores resultados. Un dropout del 30% y un batch size de 32 también parecen ser valores que favorecen un bajo error de validación. No hay una tendencia clara para otros tamaños de batch
# Predicciones en train, val y test
plot_model_results_plotly(y_train_resd, train_predictions_resd, y_val_resd, val_predictions_resd, y_test_resd, test_predictions_resd, data.index, len(y_train_resd), len(y_val_resd), 'Modelo híbrido')
Esta gráfica muestra los resultados del modelo híbrido (ARIMA + MLP) aplicado a la serie de tiempo del precio del gas natural. Se comparan los datos reales con las predicciones del modelo en los conjuntos de entrenamiento, validación y prueba. Al ser un modelo híbrido, se muestran dos líneas para cada conjunto: una para los datos reales y otra para la predicción del modelo.
Azul (Datos de entrenamiento) / Rojo (Modelo entrenado) La línea azul representa los datos reales del conjunto de entrenamiento, mientras que la línea roja representa las predicciones del modelo híbrido para esos mismos datos. Se observa que el modelo entrenado sigue bastante de cerca a los datos reales, lo que sugiere un buen ajuste.
Verde (Datos de validación) / Morado (Predicción validación): La línea verde muestra los datos reales del conjunto de validación. La línea morada muestra las predicciones del modelo híbrido. Las predicciones se acercan al conjunto de validación.
Naranja claro (Datos de prueba) / Azul Aguamarina (Predicción prueba): Similar a lo anterior, pero para el conjunto de prueba. La línea naranja claro muestra los datos reales del conjunto de prueba. La línea azul aguamarina representa las predicciones del modelo híbrido. Se observa que el modelo captura algunas tendencias, pero la precisión es más baja que en los conjuntos de entrenamiento y validación, lo cual puede ser debido a la presencia de valores atípicos o la propia naturaleza de los datos.
En conclusión, El modelo híbrido presenta un ajuste razonable a los datos de entrenamiento y validación. Las predicciones están relativamente cerca de los valores reales. La precisión en el conjunto de prueba es menor que en los conjuntos de entrenamiento y validación, lo que sugiere que el modelo puede estar ligeramente sobreajustado. La alta variabilidad de los datos de prueba hace que las predicciones sean más difíciles. También podrían existir factores externos como el impacto de las fluctuaciones de las variables macroeconómicas que no fueron incluidos en el modelo.
Combinación de predicciones#
Sumamos los residuos predichos a las predicciones ARIMA.
A las prediicones de ARIMA se le quitan los primero 5 datos para ajustarlo con las predicciones de los residuos.
train_join=train_predictions_resd + y_train_pred[5:]
val_join=val_predictions_resd + y_val_pred[5:]
test_join= test_predictions_resd+ y_test_pred[5:]
plot_model_results_plotly(y_train[5:],train_join , y_val[5:], val_join, y_test[5:], test_join, data.index, len(y_train), len(y_val), 'predicciones parciales')
Esta gráfica muestra las predicciones del modelo híbrido (ARIMA + MLP) en los conjuntos de entrenamiento, validación y prueba. A diferencia de la gráfica del modelo híbrido anterior, esta gráfica se centra en las predicciones de la serie temporal original y no solo en los residuos. Se elimina una parte inicial (los primeros 5 puntos de datos) de cada conjunto para alinear las predicciones del modelo MLP con las predicciones del modelo ARIMA, dado que el modelo MLP fue aplicado a los residuos del modelo ARIMA y estos residuos no existen para los primeros 5 puntos.
Azul (Datos de entrenamiento): Los valores reales del conjunto de entrenamiento a partir del sexto punto de datos.
Rojo (Modelo entrenado): Las predicciones del modelo híbrido para los datos de entrenamiento.
Verde (Datos de validación): Los valores reales del conjunto de validación.
Morado (Predicción validación): Las predicciones del modelo híbrido para el conjunto de validación.
Naranja claro (Datos de prueba): Los valores reales del conjunto de prueba.
Azul Aguamarina (Predicción prueba): Las predicciones del modelo híbrido para el conjunto de prueba.
La gráfica muestra cómo el modelo híbrido se ajusta a la serie temporal. Se puede observar que:
El modelo sigue la tendencia general: Tanto en entrenamiento como en validación, el modelo híbrido captura la tendencia general de la serie temporal.
La precisión varía: La precisión del modelo es mejor en el conjunto de entrenamiento, donde el ajuste es más cercano a los datos reales. La precisión en el conjunto de validación también es alta. Sin embargo, en el conjunto de prueba, la precisión es más variable, con algunas desviaciones significativas entre las predicciones y los valores reales. Esto puede ser porque el modelo, aunque bueno en tendencia, no ha capturado bien algunos detalles.
Modelo original#
Calculamos los residuos del modelo original y analizamos las métricas finales (MSE, RMSE, MAPE) para la serie completa. Así mismo, se realizan gráficos de residuos e histogramas y se llevan a cabo pruebas de hipótesis sobre los residuos.
resid_join_train= y_train[5:]- train_join
resid_join_val= y_val[5:]- val_join
resid_join_test= y_test[5:]-test_join
from sklearn.metrics import mean_squared_error, mean_absolute_percentage_error
from scipy.stats import shapiro, ttest_1samp
from statsmodels.stats.diagnostic import acorr_ljungbox, het_breuschpagan
import statsmodels.api as sm
# Función para analizar residuales
def analyze_residuals(residuals, y_true, predictions, dataset_name=""):
# Calcular MAPE y MSE
mape = mean_absolute_percentage_error(y_true[5:], predictions) # Ignorando los primeros 5 valores como en los residuales
mse = mean_squared_error(y_true[5:], predictions)
# Pruebas de Hipótesis sobre los Supuestos
print(f"----- Pruebas de Hipótesis sobre los Supuestos para {dataset_name} -----")
# Prueba de Shapiro-Wilk para normalidad
shapiro_stat, shapiro_p = shapiro(residuals)
print(f"Shapiro-Wilk p-value ({dataset_name}): {shapiro_p:.4f}")
if shapiro_p < 0.05:
print("Conclusión: NO sigue una distribución normal.")
else:
print("Conclusión: Sigue una distribución normal.")
# Prueba de Ljung-Box para autocorrelación
ljungbox_results = acorr_ljungbox(residuals, lags=[10]) # Probando los primeros 10 retardos
ljungbox_p = ljungbox_results["lb_pvalue"].iloc[0]
print(f"Ljung-Box p-value ({dataset_name}): {ljungbox_p:.4f}")
if ljungbox_p < 0.05:
print("Conclusión: Autocorrelación significativa.")
else:
print("Conclusión: No hay autocorrelación significativa.")
# Prueba de Breusch-Pagan para heterocedasticidad
_, bp_pvalue, _, _ = het_breuschpagan(residuals, sm.add_constant(predictions))
print(f"Breusch-Pagan p-value ({dataset_name}): {bp_pvalue:.4f}")
if bp_pvalue < 0.05:
print("Conclusión: Varianza no constante.")
else:
print("Conclusión: Varianza constante.")
# Resultados de MAPE y MSE
print(f"\nResultados de Error para {dataset_name}:")
print(f"MAPE ({dataset_name}): {mape:.4f}")
print(f"MSE ({dataset_name}): {mse:.4f}\n")
# Calcular los residuales de entrenamiento, validación y prueba
resid_join_train = y_train[5:] - train_join
resid_join_val = y_val[5:] - val_join
resid_join_test = y_test[5:] - test_join
# Análisis de los residuales para cada conjunto
analyze_residuals(resid_join_train, y_train, train_join, dataset_name="Entrenamiento")
analyze_residuals(resid_join_val, y_val, val_join, dataset_name="Validación")
analyze_residuals(resid_join_test, y_test, test_join, dataset_name="Prueba")
----- Pruebas de Hipótesis sobre los Supuestos para Entrenamiento -----
Shapiro-Wilk p-value (Entrenamiento): 0.0000
Conclusión: NO sigue una distribución normal.
Ljung-Box p-value (Entrenamiento): 0.7537
Conclusión: No hay autocorrelación significativa.
Breusch-Pagan p-value (Entrenamiento): 0.0000
Conclusión: Varianza no constante.
Resultados de Error para Entrenamiento:
MAPE (Entrenamiento): 0.0252
MSE (Entrenamiento): 0.0433
----- Pruebas de Hipótesis sobre los Supuestos para Validación -----
Shapiro-Wilk p-value (Validación): 0.0000
Conclusión: NO sigue una distribución normal.
Ljung-Box p-value (Validación): 0.0151
Conclusión: Autocorrelación significativa.
Breusch-Pagan p-value (Validación): 0.0000
Conclusión: Varianza no constante.
Resultados de Error para Validación:
MAPE (Validación): 0.0226
MSE (Validación): 0.0077
----- Pruebas de Hipótesis sobre los Supuestos para Prueba -----
Shapiro-Wilk p-value (Prueba): 0.0000
Conclusión: NO sigue una distribución normal.
Ljung-Box p-value (Prueba): 0.0700
Conclusión: No hay autocorrelación significativa.
Breusch-Pagan p-value (Prueba): 0.0000
Conclusión: Varianza no constante.
Resultados de Error para Prueba:
MAPE (Prueba): 0.0359
MSE (Prueba): 0.0570
Estos resultados muestran las evaluaciones de diagnóstico y las métricas de error para el modelo de pronóstico (modelo híbrido ARIMA + MLP), evaluado en los conjuntos de entrenamiento, validación y prueba, así:
I. Pruebas de Hipótesis sobre los Supuestos:
Estas pruebas evalúan si los residuales del modelo cumplen con los supuestos clásicos de los modelos de regresión, aunque no son críticos para el funcionamiento de las Redes Neuronales.
Shapiro-Wilk (p-value): Esta prueba verifica la normalidad de los residuales. Un p-valor menor a 0.05 indica que se rechaza la hipótesis nula de normalidad. En todos los conjuntos (entrenamiento, validación y prueba), el p-valor es 0.0000, indicando que los residuales no siguen una distribución normal.
Ljung-Box (p-value): Esta prueba detecta autocorrelación en los residuales. Un p-valor menor a 0.05 indica autocorrelación significativa.
Entrenamiento: p-valor = 0.7537 (No hay autocorrelación significativa). Validación: p-valor = 0.0151 (Autocorrelación significativa). Prueba: p-valor = 0.0700 (No hay autocorrelación significativa).
Breusch-Pagan (p-value): Esta prueba verifica la homocedasticidad (varianza constante) de los residuales. Un p-valor menor a 0.05 indica heterocedasticidad (varianza no constante). En todos los conjuntos, el p-valor es 0.0000, lo que indica que la varianza de los residuales no es constante.
II. Resultados de Error:
MAPE: Mide el error promedio como porcentaje del valor real. Valores menores indican mayor precisión.
Entrenamiento: 0.0252 Validación: 0.0226 Prueba: 0.0359
MSE:Mide el promedio de los cuadrados de las diferencias entre los valores predichos y los reales. Valores menores indican mayor precisión.
Entrenamiento: 0.0433 Validación: 0.0077 Prueba: 0.0570
El modelo tiene un rendimiento razonable en el conjunto de validación (MAPE bajo y MSE bajo).
Cuando los residuos de un modelo no siguen una distribución normal y presentan una varianza no constante (heterocedasticidad), pero muestran independencia (es decir, no tienen autocorrelación), esto suele indicar que el modelo ha capturado adecuadamente las relaciones temporales en los datos, pero que existen otros factores no modelados que afectan la forma y dispersión de los residuos.
1. No Normalidad en los Residuos
Naturaleza de los Datos: En muchas aplicaciones del mundo real, los datos pueden no ajustarse a una distribución normal, especialmente en series temporales de eventos raros, datos financieros, datos económicos, entre otros. La falta de normalidad en los residuos puede ser una característica intrínseca de los datos, y no necesariamente un problema si el modelo logra capturar la independencia de los residuos. Distribuciones con Colas Largas o Sesgadas: Si los datos contienen valores extremos o están sesgados, es común que los residuos muestren colas largas o asimetría. Esto se traduce en una falta de normalidad en los residuos, pero no implica necesariamente una dependencia temporal.
Modelo Adecuado pero Datos No Normales: El hecho de que los residuos no sigan una distribución normal, pero sean independientes, sugiere que el modelo ha capturado bien la estructura subyacente de los datos. La no normalidad podría simplemente reflejar que los datos tienen una distribución diferente, como log-normal, exponencial, entre otras.
2. Varianza No Constante (Heterocedasticidad)
Cambios en la Variabilidad del Proceso: Algunos procesos, especialmente en series temporales financieras y económicas, tienen periodos de alta y baja volatilidad. Esto se traduce en una varianza no constante de los residuos. Sin embargo, si estos cambios en la varianza no están correlacionados temporalmente, es posible que no haya dependencia entre ellos, resultando en residuos heterocedásticos pero independientes.
Eventos Específicos o Cambios Estructurales: La varianza no constante puede deberse a eventos atípicos o cambios en las condiciones subyacentes del sistema (por ejemplo, crisis económicas o eventos estacionales). Si bien estos eventos afectan la varianza de los residuos, no necesariamente introducen una correlación temporal.
Independencia de los Residuos
Eficiencia del Modelo: La independencia de los residuos sugiere que el modelo ha capturado correctamente la estructura temporal de los datos. No hay autocorrelación significativa en los residuos, lo cual es un indicador positivo de que el modelo ha logrado capturar las dependencias temporales presentes en la serie.
Los residuos obtenidos del modelo muestran que no siguen una distribución normal y presentan una varianza no constante, lo cual puede deberse a la naturaleza de los datos y a la presencia de eventos específicos o periodos de alta y baja volatilidad. Sin embargo, la independencia de los residuos indica que el modelo ha capturado correctamente las relaciones temporales presentes en los datos, sin dejar patrones significativos sin modelar. Aunque los residuos no son normales y tienen heterocedasticidad, el hecho de que sean independientes sugiere que el modelo es adecuado para predecir la media del proceso. Esto puede ser aceptable en aplicaciones donde el objetivo principal es la precisión de la media y la independencia de los errores, más que la normalidad o varianza constante.
En resumen, cuando los residuos no son normales y tienen varianza no constante, pero son independientes, la no normalidad y la heterocedasticidad pueden ser características propias de los datos y no necesariamente afectan la capacidad del modelo para capturar la estructura temporal, la independencia de los residuos sugiere que el modelo es adecuado para predecir el comportamiento promedio del sistema. Aunque el modelo puede mejorarse para capturar la varianza cambiante (por ejemplo, con un modelo que incluya heterocedasticidad), la independencia de los errores implica que los valores de predicción de la media no están sesgados.
EL modelo tuvo un error menor a todos los modelos anetriormente implementados gracias a la combianción de las mejores caracteríasticas de ARIMA Y MLP.